package org.apache.http.client.fluent;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;

public class Request
{
  public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
  public static final Locale DATE_LOCALE = Locale.US;
  public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
  private final HttpRequestBase request;
  private final HttpParams localParams;
  private SimpleDateFormat dateFormatter;

  public static Request Get(URI uri)
  {
    return new Request(new HttpGet(uri));
  }

  public static Request Get(String uri) {
    return new Request(new HttpGet(uri));
  }

  public static Request Head(URI uri) {
    return new Request(new HttpHead(uri));
  }

  public static Request Head(String uri) {
    return new Request(new HttpHead(uri));
  }

  public static Request Post(URI uri) {
    return new Request(new HttpPost(uri));
  }

  public static Request Post(String uri) {
    return new Request(new HttpPost(uri));
  }

  public static Request Put(URI uri) {
    return new Request(new HttpPut(uri));
  }

  public static Request Put(String uri) {
    return new Request(new HttpPut(uri));
  }

  public static Request Trace(URI uri) {
    return new Request(new HttpTrace(uri));
  }

  public static Request Trace(String uri) {
    return new Request(new HttpTrace(uri));
  }

  public static Request Delete(URI uri) {
    return new Request(new HttpDelete(uri));
  }

  public static Request Delete(String uri) {
    return new Request(new HttpDelete(uri));
  }

  public static Request Options(URI uri) {
    return new Request(new HttpOptions(uri));
  }

  public static Request Options(String uri) {
    return new Request(new HttpOptions(uri));
  }

  Request(HttpRequestBase request)
  {
    this.request = request;
    this.localParams = request.getParams();
  }

  HttpRequestBase getHttpRequest() {
    return this.request;
  }

  public Response execute() throws ClientProtocolException, IOException {
    return new Response(Executor.CLIENT.execute(this.request));
  }

  public void abort() throws UnsupportedOperationException {
    this.request.abort();
  }

  public Request addHeader(Header header)
  {
    this.request.addHeader(header);
    return this;
  }

  public Request addHeader(String name, String value) {
    this.request.addHeader(name, value);
    return this;
  }

  public Request removeHeader(Header header) {
    this.request.removeHeader(header);
    return this;
  }

  public Request removeHeaders(String name) {
    this.request.removeHeaders(name);
    return this;
  }

  public Request setHeaders(Header[] headers) {
    this.request.setHeaders(headers);
    return this;
  }

  public Request setCacheControl(String cacheControl) {
    this.request.setHeader("Cache-Control", cacheControl);
    return this;
  }

  private SimpleDateFormat getDateFormat() {
    if (this.dateFormatter == null) {
      this.dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", DATE_LOCALE);
      this.dateFormatter.setTimeZone(TIME_ZONE);
    }
    return this.dateFormatter;
  }

  public Request setDate(Date date) {
    this.request.setHeader("Date", getDateFormat().format(date));
    return this;
  }

  public Request setIfModifiedSince(Date date) {
    this.request.setHeader("If-Modified-Since", getDateFormat().format(date));
    return this;
  }

  public Request setIfUnmodifiedSince(Date date) {
    this.request.setHeader("If-Unmodified-Since", getDateFormat().format(date));
    return this;
  }

  public Request config(String param, Object object)
  {
    this.localParams.setParameter(param, object);
    return this;
  }

  public Request removeConfig(String param) {
    this.localParams.removeParameter(param);
    return this;
  }

  public Request version(HttpVersion version)
  {
    return config("http.protocol.version", version);
  }

  public Request elementCharset(String charset) {
    return config("http.protocol.element-charset", charset);
  }

  public Request useExpectContinue() {
    return config("http.protocol.expect-continue", Boolean.valueOf(true));
  }

  public Request userAgent(String agent) {
    return config("http.useragent", agent);
  }

  public Request socketTimeout(int timeout)
  {
    return config("http.socket.timeout", Integer.valueOf(timeout));
  }

  public Request connectTimeout(int timeout) {
    return config("http.connection.timeout", Integer.valueOf(timeout));
  }

  public Request staleConnectionCheck(boolean b) {
    return config("http.connection.stalecheck", Boolean.valueOf(b));
  }

  public Request viaProxy(HttpHost proxy)
  {
    return config("http.route.default-proxy", proxy);
  }

  public Request body(HttpEntity entity)
  {
    if ((this.request instanceof HttpEntityEnclosingRequest))
      ((HttpEntityEnclosingRequest)this.request).setEntity(entity);
    else {
      throw new IllegalStateException(this.request.getMethod() + " request cannot enclose an entity");
    }

    return this;
  }

  public Request bodyForm(Iterable<? extends NameValuePair> formParams, Charset charset) {
    return body(new UrlEncodedFormEntity(formParams, charset));
  }

  public Request bodyForm(Iterable<? extends NameValuePair> formParams) {
    return bodyForm(formParams, HTTP.DEF_CONTENT_CHARSET);
  }

  public Request bodyForm(NameValuePair[] formParams) {
    return bodyForm(Arrays.asList(formParams), HTTP.DEF_CONTENT_CHARSET);
  }

  public Request bodyString(String s, ContentType contentType) {
    return body(new StringEntity(s, contentType));
  }

  public Request bodyFile(File file, ContentType contentType) {
    return body(new FileEntity(file, contentType));
  }

  public Request bodyByteArray(byte[] b) {
    return body(new ByteArrayEntity(b));
  }

  public Request bodyByteArray(byte[] b, int off, int len) {
    return body(new ByteArrayEntity(b, off, len));
  }

  public Request bodyStream(InputStream instream) {
    return body(new InputStreamEntity(instream, -1L));
  }

  public Request bodyStream(InputStream instream, ContentType contentType) {
    return body(new InputStreamEntity(instream, -1L, contentType));
  }

  public String toString()
  {
    return this.request.getRequestLine().toString();
  }
}